// check header pointer
owl_bool owl_check_smg_btp_header_pointer(owl_byte * src, owl_umax size)
{
  return (src != NULL && size >= sizeof(owl_smg_btp_header));
}

// check pointer
owl_bool owl_check_smg_btp_pointer(owl_byte * src, owl_umax size)
{
  return (src != NULL && size >= sizeof(owl_smg_btp));
}

// check all
owl_bool owl_check_smg_btp_all(owl_smg_btp * btp, owl_umax size)
{
  // check params
  if (owl_check_smg_btp_pointer((void *) btp, size) == owl_false
      || owl_check_smg_tpt1_all(btp -> tpt1, sizeof(owl_smg_tpt1)) == owl_false)
    goto err;
  
  // header struct is not obligatory
  if (btp -> header != NULL) {
    if (owl_check_smg_btp_header_pointer((void *) btp -> header,
                                         sizeof(owl_smg_btp_header)) == owl_false)
      goto err;
  } else
    goto end;
  
  // check header magic and file type
  if (((btp -> header -> magic != OWL_SMG_BTP_MAGIC[0][0])
       && (btp -> header -> file_type != OWL_SMG_BTP_FILE_TYPE[0][0]))
      &&
      ((btp -> header -> magic != OWL_SMG_BTP_MAGIC[0][1])
       && (btp -> header -> file_type != OWL_SMG_BTP_FILE_TYPE[0][1])))
    goto err;
  
  // check file size and section count
  owl_umax tmp = 32 + (btp -> tpt1 -> ext == NULL ?
                       owl_get_smg_tpt1_bin_size(btp -> tpt1) :
                       btp -> tpt1 -> ext -> size);
  if (btp -> header -> file_size != tmp)
    goto err;  
  if (btp -> header -> section_count != OWL_SMG_BTP_SECTION_COUNT)
    goto err;
  
  // ignore unknown 1
  end:
  return owl_true;
  err:
  return owl_false;
}

// create
owl_smg_btp * owl_create_smg_btp(owl_str * raw, owl_endian endian)
{
  // check params
  if (owl_check_str_all(raw, sizeof(owl_str)) == owl_false)
    return NULL;
  
  // create new structure and fill it while checking it
  owl_smg_btp * new = NULL;  
  owl_mem_space * tmp_sp = owl_create_mem_space(sizeof(owl_smg_btp));
  if (tmp_sp == NULL) goto err;
  new = (void *) tmp_sp -> ptr;
  tmp_sp = owl_create_mem_space(sizeof(owl_smg_btp_header));
  if (tmp_sp == NULL) goto err;
  new -> header = (void *) tmp_sp -> ptr;
  tmp_sp = owl_create_mem_space(16);
  if (tmp_sp == NULL) goto err;
  new -> header -> unknown1 = (void *) tmp_sp -> ptr;
  
  // variable to help read raw
  owl_bitarr bits = {&((owl_mem_space) {raw -> arr -> ptr, raw -> arr -> size}), 0, 0};
  
  // btp magic, file type and endian detection
  if (bits.arr -> size < 32) {OWL_SMG_BTP_ERR_CD = OWL_SMG_BTP_ERR_STR_SIZE; goto err;}
  new -> header -> magic = owl_get_std_uint32_mw(&bits, OWL_ENDIAN_BIG).integer.u;
  if (new -> header -> magic != OWL_SMG_BTP_MAGIC[0][endian == OWL_ENDIAN_BIG ? 0 : 1])
    {OWL_SMG_BTP_ERR_CD = OWL_SMG_BTP_ERR_MAGIC; goto err;}
  new -> header -> file_type = owl_get_std_uint32_mw(&bits, OWL_ENDIAN_BIG).integer.u;
  if (new -> header -> file_type != OWL_SMG_BTP_FILE_TYPE[0][endian == OWL_ENDIAN_BIG ? 0 : 1])
    {OWL_SMG_BTP_ERR_CD = OWL_SMG_BTP_ERR_FILE_TYPE; goto err;}
  // file size and section count
  new -> header -> file_size = owl_get_std_uint32_mw(&bits, endian).integer.u;
  if (new -> header -> file_size % 32 != 0 || new -> header -> file_size == 0 || new -> header -> file_size > raw -> arr -> size)
    {OWL_SMG_BTP_ERR_CD = OWL_SMG_BTP_ERR_FILE_TYPE; goto err;}
  new -> header -> section_count = owl_get_std_uint32_mw(&bits, endian).integer.u;
  if (new -> header -> section_count != OWL_SMG_BTP_SECTION_COUNT)
    {OWL_SMG_BTP_ERR_CD = OWL_SMG_BTP_ERR_SECTION_COUNT; goto err;}
  // unknown 1
  owl_copy_bytarr(&((owl_bytarr) {bits.arr -> ptr, 16}), &((owl_bytarr) {new -> header -> unknown1, 16}));
  
  // create the tpt1 structure
  raw -> arr -> ptr += 32;
  raw -> arr -> size -= 32;
  new -> tpt1 = owl_create_smg_tpt1(raw, endian);
  if (new -> tpt1 == NULL) {
    OWL_SMG_BTP_ERR_CD = OWL_SMG_BTP_ERR_TPT1_SECTION;
    raw -> arr -> ptr -= 32;
    raw -> arr -> size += 32;
    goto err;
  }
  raw -> arr -> ptr -= 32;
  raw -> arr -> size += 32;
  
  // done!
  return new;
  err:
  if (new != NULL) {
    if (new -> header != NULL)
      owl_free_mem_space(&((owl_mem_space) {new -> header -> unknown1, 1}));
    owl_free_mem_space(&((owl_mem_space) {(void *) new -> header, 1}));
    owl_free_smg_tpt1(new -> tpt1);
  }
  owl_free_mem_space(&((owl_mem_space) {(void *) new, 1}));
  owl_free_mem_space(tmp_sp);
  return NULL;
}

// free
owl_umax owl_free_smg_btp(owl_smg_btp * btp)
{
  // check params
  if (owl_check_smg_btp_all(btp, sizeof(owl_smg_btp)) == owl_false)
    return 0;
  
  // free the memory used by the structure
  if (btp -> header != NULL) {
  owl_free_mem_space(&((owl_mem_space) {btp -> header -> unknown1, 1}));
  owl_free_mem_space(&((owl_mem_space) {(void *) btp -> header, 1}));
  }
  owl_free_smg_tpt1(btp -> tpt1);
  return owl_free_mem_space(&((owl_mem_space) {(void *) btp, 1}));
}

// print
owl_umax owl_print_smg_btp(owl_byte * src, owl_umax size)
{
  printf("OWL_SMG_BTP struct info:\n");
  printf("  Struct info: %p, %llu\n", src, size);
  if (owl_check_smg_btp_all((void *) src, size) == owl_false)
    return 0;
  
  // else print the structure
  owl_smg_btp * btp = (void *) src;
  
  // header
  if (btp -> header == NULL) goto tpt1;
  printf("  OWL_SMG_BTP_HEADER struct info:\n");
  printf("    Struct info: %p, %lu\n", btp -> header, sizeof(owl_smg_btp_header));
  printf("    Magic (hex): %08llX\n", btp -> header -> magic);
  printf("    File type (hex): %08llX\n", btp -> header -> file_type);
  printf("    File size: %llu\n", btp -> header -> file_size);
  printf("    Section count: %llu\n", btp -> header -> section_count);
  printf("    Unknown 1 (hex):");
  for (owl_byte i = 0; i < OWL_SMG_BTP_UNKNOWN1_LENGTH; i++)
    printf(" %02X", (btp -> header -> unknown1)[i]);
  putchar('\n');
  // tpt1
  tpt1:
  printf("  OWL_SMG_TPT1 struct info:\n");
  printf("    Struct info: %p, %lu\n", btp -> tpt1, sizeof(owl_smg_tpt1));
  printf("    Use owl_print_smg_tpt1() for the rest.\n");
  
  // done!
  return sizeof(owl_smg_btp);
}

// compare
owl_char owl_comp_smg_btp(owl_byte * btp1, owl_byte * btp2)
{
  return owl_comp_err;
}

// get the header struct in a btp struct
owl_bool owl_get_smg_btp_header(owl_smg_btp * btp)
{
  // check params
  if (owl_check_smg_btp_all(btp, sizeof(owl_smg_btp)) == owl_false
      || btp -> header != NULL)
    return owl_false;
  
  // allocate space for the struct and fill it
  owl_mem_space * tmp_sp = owl_create_mem_space(sizeof(owl_smg_btp_header));
  if (tmp_sp == NULL) goto err;
  btp -> header = (void *) tmp_sp -> ptr;
  tmp_sp = owl_create_mem_space(16);
  if (tmp_sp == NULL) goto err;
  btp -> header -> unknown1 = (void *) tmp_sp -> ptr;
  
  // add the data as big endian
  btp -> header -> magic = OWL_SMG_BTP_MAGIC[0][0];
  btp -> header -> file_type = OWL_SMG_BTP_FILE_TYPE[0][0];
  btp -> header -> file_size = (btp -> tpt1 -> ext != NULL ?
                                btp -> tpt1 -> ext -> size :
                                owl_get_smg_tpt1_bin_size(btp -> tpt1)) + 32;
  btp -> header -> section_count = OWL_SMG_BTP_SECTION_COUNT;
  for (owl_byte i = 0; i < OWL_SMG_BTP_UNKNOWN1_LENGTH; i++)
    (btp -> header -> unknown1)[i] = OWL_SMG_BTP_UNKNOWN1[i];
  
  // end
  return owl_true;
  err:
  if (btp -> header != NULL) {
    owl_free_mem_space(&((owl_mem_space) {btp -> header -> unknown1, 1}));
    owl_free_mem_space(&((owl_mem_space) {(void *) btp -> header, 1}));
  }
  owl_free_mem_space(tmp_sp);
  return owl_false;
}

// get the byte size of an encoded btp structure
owl_umax owl_get_smg_btp_bin_size(owl_smg_btp * btp)
{
  // check params
  if (owl_check_smg_btp_all(btp, sizeof(owl_smg_btp)) == owl_false)
    return 0;
  // return the size
  return (btp -> header != NULL ?
          btp -> header -> file_size :
          (btp -> tpt1 -> ext != NULL ?
           btp -> tpt1 -> ext -> size + 32 :
           owl_get_smg_tpt1_bin_size(btp -> tpt1) + 32));
}

// get the error code of a btp string
owl_smg_btp_err_cd owl_get_err_cd_smg_btp(owl_str * raw, owl_endian endian)
{
  // reuse owl_create_smg_btp()
  owl_smg_btp * tmp = owl_create_smg_btp(raw, endian);
  owl_free_smg_btp(tmp);
  return OWL_SMG_TPT1_ERR_CD;
}

// get a smg_btp from minimum args
owl_smg_btp * owl_get_smg_btp_min_args(owl_smg_tpt1 * tpt1)
{
  // check params
  if (owl_check_smg_tpt1_all(tpt1, sizeof(owl_smg_tpt1)) == owl_false)
    return NULL;
  
  // create a trimmed functional structure, will copy everything from tpt1
  owl_smg_btp * btp = NULL;
  owl_mem_space * tmp_sp = owl_create_mem_space(sizeof(owl_smg_btp));
  if (tmp_sp == NULL) goto err;
  btp = (void *) tmp_sp -> ptr;
  btp -> tpt1 = owl_get_smg_tpt1_min_args(tpt1 -> main -> loop_mode,
                                          tpt1 -> main -> anim_length,
                                          tpt1 -> main -> mat_count,
                                          tpt1 -> main -> tex_indexes_length,
                                          tpt1 -> main -> mat_anim_data_main,
                                          tpt1 -> main -> tex_indexes,
                                          tpt1 -> main -> mat_indexes,
                                          tpt1 -> main -> mat_names -> main -> names);
  if (btp -> tpt1 == NULL) goto err;
  
  // done!
  return btp;
  err:
  owl_free_mem_space(tmp_sp);
  return NULL;
}

// encode a btp structure into a string
owl_str * owl_get_smg_btp_as_str(owl_smg_btp * btp, owl_endian endian)
{
  // check params
  if (owl_check_smg_btp_all(btp, sizeof(owl_smg_btp)) == owl_false
      || owl_check_endian(endian) == owl_false)
    return NULL;
  
  // variables to be used
  owl_mem_space * str_sp = NULL;
  owl_str * tmp = NULL;
  
  // make room for the string
  owl_umax str_size = owl_get_smg_btp_bin_size(btp);
  str_sp = owl_create_mem_space(str_size);
  if (str_sp == NULL) goto err;
  
  // encode a btp struct into a string
  tmp = owl_get_smg_tpt1_as_str(btp -> tpt1, endian);
  if (tmp == NULL) goto err;
  
  // copy data from new into its respective position in str_sp
  str_sp -> ptr += 32;
  owl_copy_bytarr(tmp -> arr, str_sp);
  str_sp -> ptr -= 32;
  owl_free_str(tmp);
  
  // create the header struct if not present
  if (btp -> header == NULL && owl_get_smg_btp_header(btp) == owl_false) goto err;
  
  // write the rest of the data into str_sp
  owl_bitarr write = {&((owl_bytarr) {str_sp -> ptr, str_sp -> size}), 0, 0};
  // magic, file type, file size and section count
  if (owl_write_std_uint32_mv(OWL_SMG_BTP_MAGIC[0][0], &write, endian) == owl_false) goto err;
  if (owl_write_std_uint32_mv(OWL_SMG_BTP_FILE_TYPE[0][0], &write, endian) == owl_false) goto err;
  if (owl_write_std_uint32_mv(btp -> header -> file_size, &write, endian) == owl_false) goto err;
  if (owl_write_std_uint32_mv(btp -> header -> section_count, &write, endian) == owl_false) goto err;
  // unknown 1
  for (owl_byte i = 0; i < OWL_SMG_BTP_UNKNOWN1_LENGTH; i++)
    (write.arr -> ptr)[i] = OWL_SMG_BTP_UNKNOWN1[i];
    
  // create the encoded string
  tmp = owl_create_str(str_sp -> ptr, str_sp -> size, OWL_WCHAR_ENC_BIN);
  if (tmp == NULL) goto err;
  // free the temporal space
  owl_free_mem_space(str_sp);
  
  // done!
  return tmp;
  err:
  owl_free_mem_space(str_sp);
  owl_free_str(tmp);
  return NULL;
}
